use std::fs::File;
use std::io::Write;
use std::iter::successors;
use std::mem::size_of_val;

///  General Phone number generation struct
///
/// ## Test Example
///
/// ```
/// extern crate telnum_gen; // For tests only
/// use telnum_gen::phone_gen::PhoneGen;
/// fn main () {
///     let gen = PhoneGen::new("1800".to_string(), 9, "test.txt".to_string());
///     assert_eq!("1800", gen.prefix);
///     assert_eq!(9, gen.combinations);
///     assert_eq!("test.txt", gen.file);
/// }
/// ```
pub struct PhoneGen {
    /// Specify string prefix.
    /// For example, if prefix `1800` is specified
    /// Then only numbers in `1800XXXXXXX` will be generated
    pub prefix: String,
    /// Specify number of cartesian combinations
    /// This is the total count of `X` digits that need to be generated
    pub combinations: usize,
    /// Output file name with valid path
    pub file: String,
}

// impl<'a, T> Iterator for PhoneGen<'a, T> {
//     type Item = &'a [T];

//     fn next(&mut self) -> Option<Self::Item> {
//         if self.slice.is_empty() {
//             return None;
//         }
//         let mut len = self.len;
//         if self.rem > 0 {
//             len += 1;
//             self.rem -= 1;
//         }
//         let (chunk, rest) = self.slice.split_at(len);
//         self.slice = rest;
//         Some(chunk)
//     }
// }

impl PhoneGen {
    #[must_use]
    pub const fn new(prefix: String, combinations: usize, file: String) -> Self {
        Self {
            prefix,
            combinations,
            file,
        }
    }

    pub fn generate(&self) {
        // Generate numbers from 0 to combinations size
        let nums: Vec<i32> = successors(Some(0), |i| Some(i + 1)).take(10).collect();
        println!("Elements: {nums:?}");
        println!("Combinations: {}", &self.combinations);

        let file = open_file(&self.file);
        self.cartesian_product(&nums, &file);
    }

    fn cartesian_product(&self, numbers: &[i32], file: &File) {
        let buffer_size = 100_000;
        let mut buffer: Vec<String> = Vec::new();

        // Create a matrix of all numbers times number of combinations
        let mut v: Vec<Vec<i32>> = Vec::new();
        for _ in 1..=self.combinations {
            v.push(numbers.to_vec());
        }

        let mut indices = vec![0; v.len()];
        let mut done = false;

        while !done {
            let mut row = Vec::new();
            for (i, arr) in v.iter().enumerate() {
                row.push(arr[indices[i]]);
            }

            let r = format!(
                "{}{}",
                &self.prefix,
                row.into_iter().map(|i| i.to_string()).collect::<String>()
            );

            buffer.push(r);

            if buffer.len() == buffer_size {
                write_string_array(&buffer, file);
                buffer = Vec::new();
            }

            for i in (0..v.len()).rev() {
                indices[i] += 1;
                if indices[i] < v[i].len() {
                    break;
                }
                indices[i] = 0;
                if i == 0 {
                    done = true;
                }
            }
        }
        if !buffer.is_empty() {
            write_string_array(&buffer, file);
        }
    }
}

fn open_file(path: &String) -> File {
    File::create(path).expect("Unable to create file")
}

fn write_string_array(array: &[String], mut file: &File) {
    let buffer = array.join("\n");
    println!("Buffer size {}KB", size_of_val(&*buffer) / 1024);
    file.write_all(buffer.as_bytes())
        .expect("Unable to write file");
}
